package com.qt.manager.oj;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qt.enums.ApiExceptionEnum;
import com.qt.exception.ApiException;
import com.qt.exception.StatusFailException;
import com.qt.manager.EmailManager;
import com.qt.manager.message.SystemNoticeManager;
import com.qt.mapper.UserMapper;
import com.qt.mapper.UserRoleMapper;
import com.qt.pojo.dto.LoginDTO;
import com.qt.pojo.dto.PostResetPasswordDTO;
import com.qt.pojo.dto.RegisterDTO;
import com.qt.pojo.dto.ResetPasswordDTO;
import com.qt.pojo.entity.user.Role;
import com.qt.pojo.entity.user.Session;
import com.qt.pojo.entity.user.User;
import com.qt.pojo.entity.user.UserRole;
import com.qt.pojo.vo.RegisterCodeVO;
import com.qt.pojo.vo.UserRolesVO;
import com.qt.pojo.vo.UserVO;
import com.qt.result.R;
import com.qt.service.message.SessionService;
import com.qt.service.oj.UserRoleSerivce;
import com.qt.service.oj.UserService;
import com.qt.shiro.UserProfile;
import com.qt.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author pgw
 * @version 1.0
 * @description
 * @date Create by 2023/9/18 19:46
 */
@Component
@Slf4j
public class LoginManager {

    @Autowired
    private SystemNoticeManager systemNoticeManager;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private EmailManager emailManager;

    @Autowired
    private SessionService sessionService;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private UserRoleSerivce userRoleService;

    @Autowired
    private UserService userService;



    public UserVO login(LoginDTO loginDto , HttpServletRequest request , HttpServletResponse response)  {
        // 去掉账号密码首尾的空格
        loginDto.setPassword(loginDto.getPassword().trim());
        loginDto.setUsername(loginDto.getUsername().trim());

        String username = loginDto.getUsername();
        String password = loginDto.getPassword();
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
            throw  new ApiException(ApiExceptionEnum.USERNAME_PASSWPRD_EMPTY);
        }

        if (password.length() < 6 || password.length() > 20) {
            throw  new ApiException(ApiExceptionEnum.PASSWORD_LENGTH_ERROR);
        }
        if (username.length() > 20) {
            throw  new ApiException(ApiExceptionEnum.USERNAME_LENGTH_ERROR);
        }
        String userIpAddr = IpUtils.getUserIpAddr(request);
        String key = Constants.Account.TRY_LOGIN_NUM.getCode() + userIpAddr + "_" + userIpAddr;
        Integer tryLoginCount = (Integer) redisUtil.get(key);

        if (tryLoginCount != null && tryLoginCount >= 20) {
            throw  new ApiException(ApiExceptionEnum.USER_ACCOUNT_EXCEPTION);
        }
        //查询用户信息和权限信息
       UserRolesVO userRolesVo =  userRoleMapper.getUserRoles(null ,username );
        log.info("用户权限信息: {}" , userRolesVo);
        if (userRolesVo == null) {
            throw  new ApiException(ApiExceptionEnum.PASSWORD_NOT_MATCHING);
        }

        if (!userRolesVo.getPassword().equals(SecureUtil.md5(loginDto.getPassword()))) {
            if (tryLoginCount == null) {
                redisUtil.set(key, 1, 60 * 30); // 三十分钟不尝试，该限制会自动清空消失
            } else {
                redisUtil.set(key, tryLoginCount + 1, 60 * 30);
            }
            throw  new ApiException(ApiExceptionEnum.PASSWORD_NOT_MATCHING);
        }

        if(userRolesVo.getDeleted().equals(1)){
            throw  new ApiException(ApiExceptionEnum.USER_ACCOUNT_FORBIDDEN);
        }
        String jwt = jwtUtils.generateToken(userRolesVo.getUserId());
        response.setHeader("Authorization", jwt); //放到信息头部
        response.setHeader("Access-Control-Expose-Headers", "Authorization");

        //登录成功，解除锁定
        if (tryLoginCount != null) {
            redisUtil.del(key);
        }

        //保存会话记录
        sessionService.save(new Session()
                .setUserId(userRolesVo.getUserId())
                .setIp(IpUtils.getUserIpAddr(request))
                .setUserAgent(request.getHeader("User-Agent")));

        //检查是否异地登录
        sessionService.checkRemoteLogin(userRolesVo.getUserId());

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userRolesVo , userVO,"roles" );
        userVO.setRoleList(userRolesVo.getRoles()
                .stream()
                .map(Role::getRole).
                collect(Collectors.toList()));

        return  userVO;
    }



    public R getRegisterCode(String email) throws StatusFailException {
        email = email.trim();
        if(!EmailUtil.CheckQqMail(email)){
            throw new ApiException(ApiExceptionEnum.NONE_EMAIL_SENDER);
        }
        String lockKey = Constants.Email.REGISTER_EMAIL_LOCK + email;
        if (redisUtil.hasKey(lockKey)) {
            throw new StatusFailException("对不起，您的操作频率过快，请在" + redisUtil.getExpire(lockKey) + "秒后再次发送注册邮件！");
        }

        User user = userMapper.getByEmail(email);
        if(user != null){
             throw new ApiException(ApiExceptionEnum.EMAIL_EXIST);
        }
        String numbers = RandomUtil.randomNumbers(6); // 随机生成6位数字的组合
        redisUtil.set(Constants.Email.REGISTER_KEY_PREFIX.getValue() + email, numbers, 10 * 60);//默认验证码有效10分钟
        emailManager.sendRegisterCode(email, numbers);
        redisUtil.set(lockKey, 0, 60);

        RegisterCodeVO registerCodeVo = new RegisterCodeVO();
        registerCodeVo.setEmail(email);
        registerCodeVo.setExpire(5 * 60);

        return R.success(registerCodeVo);
    }

    @Transactional(rollbackFor = Exception.class)
    public R register(RegisterDTO registerDto) {

        String codeKey = Constants.Email.REGISTER_KEY_PREFIX.getValue() + registerDto.getEmail();
        if (!redisUtil.hasKey(codeKey)) {
            throw new ApiException(ApiExceptionEnum.CAPTCHA_NOT_FOUND);
        }

        if (!redisUtil.get(codeKey).equals(registerDto.getCode())) { //验证码判断
            throw new ApiException(ApiExceptionEnum.CAPTCHA_NOT_MATCHING);
        }

        if (StringUtils.isEmpty(registerDto.getPassword())) {
            throw  new ApiException(ApiExceptionEnum.USERNAME_PASSWPRD_EMPTY);
        }

        if (registerDto.getPassword().length() < 6 || registerDto.getPassword().length() > 20) {
            throw  new ApiException(ApiExceptionEnum.NEWPASSWORD_LENGTH_ERROR);
        }

        if (StringUtils.isEmpty(registerDto.getUsername())) {
            throw  new ApiException(ApiExceptionEnum.USERNAME_PASSWPRD_EMPTY);
        }

        if (registerDto.getUsername().length() > 20) {
           throw new ApiException(ApiExceptionEnum.USERNAME_LENGTH_ERROR);
        }

        String userId = IdUtil.simpleUUID();

        //user表插入数据
        boolean userSave = userService.save(new User()
                .setId(userId)
                .setEmail(registerDto.getEmail())
                .setPassword(registerDto.getPassword())
                .setUsername(registerDto.getUsername()));
        //user_role插入数据
        boolean roleSave = userRoleService.save(new UserRole()
                .setUserId(userId)
                .setRoleId(2L));
        if(roleSave && userSave){
            redisUtil.del(codeKey);
            systemNoticeManager.systemNoticeToNewRegitser(userId);
        }else{
            throw new ApiException(ApiExceptionEnum.REGISTER_USER_FAIL);
        }

        return  R.success();

    }

    public void  resetPassword(ResetPasswordDTO resetPasswordDto) throws StatusFailException {
        String username = resetPasswordDto.getUsername();
        String password = resetPasswordDto.getPassword();
        String code = resetPasswordDto.getCode();

        if (StringUtils.isEmpty(password) || StringUtils.isEmpty(username) || StringUtils.isEmpty(code)) {
            throw new ApiException(ApiExceptionEnum.USERNAME_PASSWPRD_CODE_EMPTY);
        }

        if (password.length() < 6 || password.length() > 20) {
            throw new ApiException(ApiExceptionEnum.NEWPASSWORD_LENGTH_ERROR);
        }

        String codeKey = Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue() + username;
        if (!redisUtil.hasKey(codeKey)) {
            throw new ApiException(ApiExceptionEnum.RESET_PASSWORD_URL_EXPIRE);
        }

        if (!redisUtil.get(codeKey).equals(code)) { //验证码判断
            throw new ApiException(ApiExceptionEnum.RESET_PASSWORD_CODE_ERROR);
        }

        LambdaUpdateWrapper<User> luw = new LambdaUpdateWrapper<>();
        luw.eq(User::getUsername, username).set(User::getPassword, SecureUtil.md5(password));
        boolean isOk = userService.update(luw);
        if (!isOk) {
            throw new ApiException(ApiExceptionEnum.RESET_PASSWORD_FAIL);
        }
        redisUtil.del(codeKey);
    }

    public void logout() {
        UserProfile userRolesVo = (UserProfile) SecurityUtils.getSubject().getPrincipal();
        jwtUtils.cleanToken(userRolesVo.getUserId());
        SecurityUtils.getSubject().logout();
    }

    public void postResetPassword(PostResetPasswordDTO postResetPasswordDto) throws StatusFailException {
        String email = postResetPasswordDto.getEmail();
        String captcha = postResetPasswordDto.getCaptcha();
        String captchaKey = postResetPasswordDto.getCaptchaKey();
        if(StrUtil.isBlank(captcha) || StrUtil.isBlank(captchaKey) || StrUtil.isBlank(email)){
           throw  new ApiException(ApiExceptionEnum.EMAIL_CODE_EMPTY);
        }
        if(!EmailUtil.CheckQqMail(email)){
            throw new ApiException(ApiExceptionEnum.NONE_EMAIL_SENDER);
        }
        String lockKey = Constants.Email.RESET_EMAIL_LOCK + email;
        if (redisUtil.hasKey(lockKey)) {
            throw new StatusFailException("对不起，您的操作频率过快，请在" + redisUtil.getExpire(lockKey) + "秒后再次发送重置邮件！");
        }

        // 获取redis中的验证码
        String redisCode = (String) redisUtil.get(captchaKey);
        if (!Objects.equals(redisCode, captcha.trim().toLowerCase())) {
           throw  new ApiException(ApiExceptionEnum.CAPTCHA_NOT_MATCHING);
        }
        User user = userMapper.getByEmail(email);
        if(user == null){
            throw new StatusFailException("对不起，该邮箱未绑定用户");
        }

        String code = IdUtil.simpleUUID().substring(0, 21); // 随机生成20位数字与字母的组合
        redisUtil.set(Constants.Email.RESET_PASSWORD_KEY_PREFIX.getValue() + user.getUsername(), code, 10 * 60);//默认链接有效10分钟
        // 发送邮件
        emailManager.sendResetPassword(user.getUsername(), code, email.trim());
        redisUtil.set(lockKey, 0, 90);

    }
}
